library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use work.global_definition.all;

architecture behave of memory is
    type TagBit is array(0 to cache_count-1) of std_ulogic;
    type PhysicalAddr is array(0 to cache_count-1) of std_ulogic_vector(ram_address_width-cache_index_width-1 downto 0);
    type CacheData is array(0 to cache_count-1) of std_ulogic_vector(ram_data_width-1 downto 0);
	type CacheFSM is (state_idle, state_refresh);
	
	signal fsm : CacheFSM := state_idle;
    signal valid   : TagBit       := (others => '0');
    signal phyaddr : PhysicalAddr := (others => (others => '0'));
    signal cache   : CacheData    := (others => (others => '0'));

    signal cache_hit : std_ulogic;
	signal x_data_ins : std_ulogic_vector(width-1 downto 0);
    signal x_address : std_ulogic_vector(15 downto 0);
begin
    
    MIO <= '0';

    process(opcode, addr_ins, addr_dat)
    begin
        if(opcode = MEMOP_NONE) then
            x_address <= addr_ins;
        else
            x_address <= addr_dat;
        end if;
    end process;

    process(x_address)
    begin
        if (unsigned(x_address) >= x"6000") then
            case x_address is
                when x"6000" => ADDR <= x"0080";
                when x"6001" => ADDR <= x"0081";
                when x"6002" => ADDR <= x"0090";
                when x"6003" => ADDR <= x"0091";
                when others  => ADDR <= (others => 'X');
            end case;
            EX_ADDR <= (others => 'X');
            REQ     <= '1';
            RAM_OE  <= '1';
        elsif (unsigned(x_address) >= x"4000") then
            EX_ADDR <= std_ulogic_vector(unsigned(x_address)-x"3FFF");  --(12 downto 0);
            ADDR    <= (others => 'X');
            REQ     <= '0';
            RAM_OE  <= '0';
        else
            ADDR    <= x_address;
            EX_ADDR <= (others => 'X');
            REQ     <= '0';
            RAM_OE  <= '1';
        end if;
    end process;

    process(opcode, clk)
    begin
        if(opcode = MEMOP_WRITE) then
            WE     <= '0';
            RAM_WE <= '0';
            DAT    <= data_dat_in;
            EX_DAT <= data_dat_in;
        else
            WE     <= '1';
            RAM_WE <= '1';
            DAT    <= (others => 'Z');
            EX_DAT <= (others => 'Z');
        end if;
    end process;

    process(opcode, DAT, EX_DAT, x_address)
    begin
        if (opcode = MEMOP_NONE) then
            data_dat_out <= x"0000";
            if valid(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0)))) = '1' and phyaddr(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0)))) = addr_ins(15 downto cache_index_width) then
                data_ins <= cache(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0))));
            else
                if (unsigned(x_address) >= x"4000") and (unsigned(x_address) < x"6000") then
					data_ins <= EX_DAT;
                    x_data_ins <= EX_DAT;  -- input from extended memory
                else
					data_ins <= DAT;
                    x_data_ins <= DAT;    -- from basic memory
                end if;
            end if;
        else
            data_ins <= x"0800";
            if(unsigned(x_address) >= x"4000")and(unsigned(x_address) < x"6000")then
                data_dat_out <= EX_DAT;  -- input from extended memory
            else
                data_dat_out <= DAT;     -- from basic memory
            end if;
        end if;
    end process;

    process(clk)
    begin
		if rising_edge(clk) then
			if fsm = state_idle then
				if opcode = MEMOP_NONE and 
				(valid(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0)))) /= '1' or 
				phyaddr(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0)))) /= addr_ins(15 downto cache_index_width)) then
					fsm <= state_refresh;
				else
					fsm <= state_idle;
				end if;
			elsif fsm = state_refresh then
				--valid(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0))))   <= '1';
				phyaddr(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0)))) <= addr_ins(15 downto cache_index_width);
				cache(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0))))   <= x_data_ins;
						
				if opcode = MEMOP_NONE and 
				(valid(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0)))) /= '1' or 
				phyaddr(to_integer(unsigned(addr_ins(cache_index_width-1 downto 0)))) /= addr_ins(15 downto cache_index_width)) then
					fsm <= state_refresh;
				else
					fsm <= state_idle;
				end if;
			end if;
		end if;
    end process;
    
end behave;
